// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2023 Yevhen Babiichuk (DustDFG)

#include <Abracadabra/View/View.hpp>
#include <Abracadabra/View/Theme.hpp>
#include <Abracadabra/View/Tube.hpp>
#include <Abracadabra/GameScene.hpp>

#include <DotSort/VectorTubeSetBuilder.hpp>

const float relSlotWidth = 5;
const float relSlotHeight = 4;

namespace View
{

View::View(sf::RenderWindow* window)
	: IView(0, 0)
	, window(window)
	, presenter(new Presenter(this, VectorTubeSetBuilder::getRandomInstance()))
{
	this->tubeSize = this->presenter->tubeSet->tubeSize;
	this->tubeCount = this->presenter->tubeSet->tubeCount;

	this->theme = new Theme();

	this->windowSize = window->getSize();

	this->calculateGeometry(tubeSize, tubeCount);

	float topBarWidth = windowSize.x;
	float topBarHeight = windowSize.y / 12 * 1.5;

	this->topBar.setSize({topBarWidth, topBarHeight});
	this->topBar.setFillColor(sf::Color(60, 60, 60));

	this->reloadTexture.loadFromFile("./res/reloadButton.png");
	this->reloadTexture.setSmooth(true);
	this->reloadButton.setSize({topBarHeight / 10 * 8, topBarHeight / 10 * 8});
	this->reloadButton.setPosition({topBarHeight / 10 * 1, topBarHeight / 10 * 1});
	this->reloadButton.setTexture(&this->reloadTexture);
}

View::~View()
{
}

void View::calculateGeometry(uint8_t tubeSize, uint8_t tubeCount)
{
	sf::Vector2f windowCenter = {
		windowSize.x / 2,
		windowSize.y / 2,
	};

	this->gameRect = {
		0,
		windowSize.y / 12 * 1.5,
		windowSize.x,
		windowSize.y / 12 * 10.5,
	};


	float gameRectInnerPadding = this->gameRect.height / 14;

	sf::FloatRect drawableRect = {
		this->gameRect.left + gameRectInnerPadding,
		this->gameRect.top + gameRectInnerPadding,
		this->gameRect.width - (gameRectInnerPadding * 2),
		this->gameRect.height - (gameRectInnerPadding * 2),
	};

	sf::Vector2f drawableRectCenter = {
		(drawableRect.width / 2) + drawableRect.left,
		(drawableRect.height / 2) + drawableRect.top,
	};

	float drawableRectAspectRatio = drawableRect.width / drawableRect.height;

	//Including floating dot
	sf::Vector2f relTubesRectSize = {
		tubeCount * relSlotWidth,
		(tubeSize + 1) * relSlotHeight,
	};

	float relTubesRectAspectRatio = relTubesRectSize.x / relTubesRectSize.y;

	float tubesRectWidth;
	float tubesRectHeight;

	if(drawableRectAspectRatio > relTubesRectAspectRatio)
	{
		tubesRectHeight = drawableRect.height;
		tubesRectWidth = tubesRectHeight * relTubesRectAspectRatio;
	}
	else
	{
		tubesRectWidth = drawableRect.width;
		tubesRectHeight = tubesRectWidth / relTubesRectAspectRatio;
	}

	float slotWidth = tubesRectWidth / tubeCount;
	float slotHeight = tubesRectHeight / (tubeSize + 1);

	float tubesRectLeft = drawableRectCenter.x - (tubesRectWidth / 2);
	float tubesRectTop = drawableRectCenter.y - (tubesRectHeight / 2) + slotHeight;

	this->tubes.reserve(tubeCount);
	for(size_t i = 0; i < tubeCount; ++i)
	{
		sf::FloatRect tubeRect = {tubesRectLeft + (slotWidth * i), tubesRectTop, slotWidth, slotHeight * tubeSize};
		this->tubes.push_back(Tube(this->tubeSize, tubeRect, this->theme));
	}
}

void View::onNewLevel()
{
	this->presenter->onLevelChange(VectorTubeSetBuilder::getRandomInstance());

	this->tubeSize = this->presenter->tubeSet->tubeSize;
	this->tubeCount = this->presenter->tubeSet->tubeCount;

	this->tubes.clear();
	this->calculateGeometry(tubeSize, tubeCount);
}

std::optional<uint8_t> View::getTubeIndex(sf::Event& event) const
{
	for(size_t i = 0; i < tubeCount; ++i)
	{
		if(this->tubes[i].contains({event.mouseButton.x, event.mouseButton.y}))
		{
			return i;
		}
	}
	return std::nullopt;
}

void View::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
	for(size_t i = 0; i < tubeCount; ++i)
	{
		this->tubes[i].fillSlotsDots(this->presenter->getDots(i));
		target.draw(this->tubes[i]);

		if(this->presenter->selectedTube)
		{
			if(*this->presenter->selectedTube == i)
			{
				this->tubes[i].fillSlotsSelectedTube();
			}
			else
			{
				this->tubes[i].fillSlotsTube();
			}
		}
		else
		{
			this->tubes[i].fillSlotsTube();
		}

		target.draw(this->tubes[i]);
	}

	target.draw(this->topBar);
	target.draw(this->reloadButton);
}

void View::draw() const
{
	this->window->draw((*this));
}

void View::onClick(sf::Event event)
{
	std::optional<uint8_t> tubeIndex = this->getTubeIndex(event);
	this->presenter->onClick(tubeIndex);
}

void View::processEvent(sf::Event event)
{
	if(event.type == sf::Event::MouseButtonPressed)
	{
		sf::Vector2f click = {event.mouseButton.x, event.mouseButton.y};

		if(this->gameRect.contains(click))
		{
			this->onClick(event);
		}
		else if(this->reloadButton.getGlobalBounds().contains(click))
		{
			this->presenter->onReload();
		}
	}
}

}
